/******************************************************************************/

package jamie.org.sil.www.usxConverter;

import java.util.Calendar;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.UUID;

import jamie.org.sil.www.usxUtils.*;


/******************************************************************************/
/**
   EPUB conversions.
   <p>

   This turns out to be rather easier than expected, since EPUB is essentially
   no more than a wrapper for XHTML files.  We can therefore run the XHTML
   converter (which I do from within the code here), generate the additional
   wrapper files needed by EPUB, and then zip up the output.
   <p>

   Note that output goes to a standard folder structure and includes some
   standard files.  We assume that all of these are already in place, and
   therefore do not need to be generated.
*/

public class UsxConverterToEpub extends UsxConverterToXhtml
{
  /****************************************************************************/
  /****************************************************************************/
  /**                                                                        **/
  /**                                 Public                                 **/
  /**                                                                        **/
  /****************************************************************************/
  /****************************************************************************/

  /**************************************************************************/
  /**
     Performs all the relevant processing.

     @throws Exception
  */

  @Override public void process () throws Exception
  {
    (new File(Config.instance().getOutputFolderPath())).mkdir();
    super.process();
    writeContentOpfFile();
    //writeCoverHtml();
    writeTocNcxFile();
    writeMimeType();
    writeMetaInf();
    zip();
  }

  
  /****************************************************************************/
  /* Initialises the features list. */

  @Override protected void initialiseFeatures () throws IOException
  {
    FeatureManager features = FeatureManager.instance();
    features.setOptionListForOutputOverallDisposition("MultipleFiles");
    features.setOptionListForOutputIntroductionDisposition("InSituInTextFile");
    features.setOptionListForOutputCrossReferencesDisposition("AtEndOfTextFile");
    features.setOptionListForOutputFootnotesDisposition("AtEndOfTextFile");
    features.setOptionListForOutputEndnotesDisposition("AtEndOfTextFile");
    features.setOptionListForOutputScriptureTextDisposition("PerBook");
    features.setAcceptDirectivesFromBookAssembly(true);
    features.setSupportsToc(true);
    features.setDefaultForOutputIntroductionDisposition("InSituInTextFile");
    
    Config.instance().setOutputOverallDisposition("MultipleFiles");
    Config.instance().setOutputScriptureTextDisposition("PerBook");
    Config.instance().setOutputTocDisposition("PerChapter");
    Config.instance().setOutputEndnotesDisposition("AtEndOfTextFile");
    Config.instance().setOutputFootnotesDisposition("AtEndOfTextFile");
    Config.instance().setOutputIntroductionDisposition("InSituInTextFile");
    Config.instance().setOutputCrossReferencesDisposition("AtEndOfTextFile");
    
    //Config.instance().setOutputFootnotesFileName(Config.instance().getIso() + "-Footnotes." + Config.instance().getOutputFileExtension());
    Config.instance().setOutputTocFileName(Config.instance().getIso() + "-Toc." + Config.instance().getOutputFileExtension());
    
    features.validateConfigurationParameters(-1);
  }
  
  
  
  
  
  /****************************************************************************/
  /****************************************************************************/
  /**                                                                        **/
  /**                                 Private                                **/
  /**                                                                        **/
  /****************************************************************************/
  /****************************************************************************/

  /****************************************************************************/
  /* Used to handle the header and trailer of the content.opf file. */

  private void writeContentOpfFile () throws IOException
  {
    /*----------------------------------------------------------------------*/
    BookElement coverElement = BookAssembly.instance().getCoverElement();
    String coverImageFileName = (null == coverElement ? "" : coverElement.getImageFileName());
    String coverOutputFileName = (null == coverElement ? "" : coverElement.getInputFileName());
    String filePath = Config.instance().getOutputFolderPath() + "content.opf";
    FileOutputStream f = new FileOutputStream(filePath);
    BufferedWriter w = new BufferedWriter(new OutputStreamWriter(f, "UTF-8"));
    String dcDate = new SimpleDateFormat("yyyy-MM-dd").format(Calendar.getInstance().getTime());
    String isoCode = Config.instance().getIso();
    String outputNotesDisposition = Config.instance().getOutputFootnotesDisposition();
    boolean notesCombined = outputNotesDisposition.equalsIgnoreCase("Combined");
    boolean notesPerTextFile = outputNotesDisposition.equalsIgnoreCase("PerTextFile");
    String uuid = m_Uuid.toString();



    /*----------------------------------------------------------------------*/
    w.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); w.newLine();
    w.write("<package xmlns=\"http://www.idpf.org/2007/opf\" version=\"2.0\" unique-identifier=\"BookId\">"); w.newLine();
    w.write("<!-- "); w.write(m_RunDateAndTime); w.write(" -->"); w.newLine();
    w.write("  <metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:opf=\"http://www.idpf.org/2007/opf\">"); w.newLine();
    w.write("    <dc:title>" + Config.instance().getDcTitle() + "</dc:title>"); w.newLine();
    w.write("    <dc:creator opf:file-as=\"Kalaam Media, Ltd.\" opf:role=\"aut\">Kalaam Media, Ltd.</dc:creator>"); w.newLine();
    w.write("    <dc:date>" + dcDate + "</dc:date>"); w.newLine();
    w.write("    <dc:identifier id=\"BookId\">" + uuid + "</dc:identifier>"); w.newLine();
    w.write("    <dc:language>x-SIL-" + isoCode + "</dc:language>"); w.newLine();
    if (0 != coverImageFileName.length()) { w.write("    <meta name=\"cover\" content=\"coverImage\"/>"); w.newLine(); }
    w.write("  </metadata>"); w.newLine();
    w.write("  <manifest>"); w.newLine();



    /*----------------------------------------------------------------------*/
    writeContentOpfFileItem1(w, "ncx", "toc.ncx", "application/x-dtbncx+xml");
    File[] fileList = (new File(Config.instance().getOutputFolderPath() + "css").listFiles());
    if (null != fileList)
    {
      int i = 0;
      for (File ff: fileList)
        writeContentOpfFileItem1(w, "css" + (++i), "css/" + ff.getName(), "text/css");
    }

    fileList = (new File(Config.instance().getOutputFolderPath() + "images").listFiles());
    if (null != fileList)
    {
      int i = 0;
      for (File ff: fileList)
      {
        String s = ff.getName();
        String name = (s.equalsIgnoreCase(coverImageFileName) ? "coverImage" : "image" + (++i));
        writeContentOpfFileItem1(w, name, "images/" + s, "image/png");
      }
    }



    /*------------------------------------------------------------------------*/
    writeContentOpfFileItem1(w, "index", "index.xhtml", "application/xhtml+xml");

    
    
    /*------------------------------------------------------------------------*/
    BookAssemblyAllOutputFilesIterator it = new BookAssemblyAllOutputFilesIterator();
    while (it.hasNext())
    {
      BookElement be = it.next();
      if ("scripture".equalsIgnoreCase(be.getType()))
      {
        if (be.getOutputFileExists(0))
        {
          writeContentOpfFileItem1(w, "B" + be.getUbsAbbreviation(), be.getOutputFileName(0), "application/xhtml+xml");
          if (notesPerTextFile) writeContentOpfFileItem1(w, be.getVernacularString() + "Notes", be.getFootnotesFileName(0), "application/xhtml+xml");
        }
      }
      else
      {
        String s = be.getVernacularStringSelector();
        String outputFileName = be.getInputFileName();
        if ("tableOfContents".equalsIgnoreCase(s) && null == outputFileName) outputFileName = Config.instance().getOutputTocFileName();
        if ("notes".equalsIgnoreCase(s) && null == outputFileName) outputFileName = Config.instance().getOutputFootnotesFileName();
        if (!"notes".equalsIgnoreCase(s) || notesPerTextFile || notesCombined)
        {
          if (0 != outputFileName.length()) writeContentOpfFileItem1(w, s, outputFileName, "application/xhtml+xml");
        }
      }
    }



    /*----------------------------------------------------------------------*/
    w.write("  </manifest>"); w.newLine();
    w.write("  <spine toc=\"ncx\">"); w.newLine();
    // $$$if (0 != coverImageFileName.length()) { w.write("    <itemref idref=\"coverImage\" linear=\"no\"/>"); w.newLine(); }
    writeContentOpfFileItem2(w, "index", "");




    /*----------------------------------------------------------------------*/
    it = new BookAssemblyAllOutputFilesIterator();
    while (it.hasNext())
    {
      BookElement be = it.next();
      if ("scripture".equalsIgnoreCase(be.getType()))
      {
        if (be.getOutputFileExists(0))
        {
          String s = be.getUbsAbbreviation();
          writeContentOpfFileItem2(w, "B" + s, "");
          if (notesPerTextFile) writeContentOpfFileItem2(w, s + "Notes", "");
        }
      }
      else
      {
        String s = be.getVernacularStringSelector();
        String outputFileName = be.getInputFileName();
        if ("tableOfContents".equalsIgnoreCase(s) && null == outputFileName) outputFileName = Config.instance().getOutputTocFileName();
        if ("notes".equalsIgnoreCase(s) && null == outputFileName) outputFileName = Config.instance().getOutputFootnotesFileName();
        if (!"notes".equalsIgnoreCase(s) || notesCombined)
        {
          if (0 != outputFileName.length()) writeContentOpfFileItem2(w, s, "");
        }
      }
    }



    /*----------------------------------------------------------------------*/
    w.write("  </spine>"); w.newLine();

    if (0 != coverImageFileName.length())
    {
      w.write("  <guide>"); w.newLine();
      w.write("    <reference type=\"cover\" title=\"Cover image\" href=\"" + coverOutputFileName + "\"/>"); w.newLine();
      w.write("  </guide>"); w.newLine();
    }

    w.write("</package>"); w.newLine();


    
    /*----------------------------------------------------------------------*/
    w.close();
    f.close();
  }


  /****************************************************************************/
  /* Write an individual item for the content.opf file. */

  private void writeContentOpfFileItem1 (BufferedWriter w,
                                         String abbrev,
                                         String fileName,
                                         String type) throws IOException
  {
    w.write("    <item id=\"" + abbrev + "\" href=\"" + fileName + "\" media-type=\"" + type + "\"/>");
    w.newLine();
  }


  /****************************************************************************/
  /* Write an individual item for the content.opf file. */

  private void writeContentOpfFileItem2 (BufferedWriter w,
                                         String abbrev,
                                         String otherBit) throws IOException
  {
    w.write("    <itemref idref=\"" + abbrev + "\"" + otherBit + "/>"); w.newLine();
  }


  /****************************************************************************/
  /* Write out the cover.html file. */

//  private void writeCoverHtml () throws IOException
//  {
//    String filePath = Config.instance().getOutputFolderPath() + "cover.html";
//    FileOutputStream f = new FileOutputStream(filePath);
//    BufferedWriter w = new BufferedWriter(new OutputStreamWriter(f, "UTF-8"));
//    String textDir = Config.instance().getTextDir().toLowerCase();
//    String rtl = textDir.equals("rtl") ? "-rtl" : "";
//
//    w.write("<?xml version='1.0' encoding = 'UTF-8'?>"); w.newLine();
//    w.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">"); w.newLine();
//    w.write("<html dir=\"" + textDir + "\" xmlns=\"http://www.w3.org/1999/xhtml\">"); w.newLine();
//    w.write("<head>"); w.newLine();
//    w.write("<title>" + Config.instance().getDcTitle() + "</title>"); w.newLine();
//    w.write("<meta http-equiv=\"Content-Type\" content=\"application/xhtml+xml; charset=utf-8\" />"); w.newLine();
//    w.write("<link rel=\"StyleSheet\" href=\"css/epub" + rtl + ".css\" type=\"text/css\" />"); w.newLine();
//    w.write("</head>"); w.newLine();
//    w.write("<body>"); w.newLine();
//    w.write("    <div>"); w.newLine();
//    w.write("      <img src=\"images/cover.png\" class=\"coverimg\" alt=\"\" />"); w.newLine();
//    w.write("    </div>"); w.newLine();
//    w.write("</body>"); w.newLine();
//    w.write("</html>"); w.newLine();
//    w.close();
//    f.close();
//  }


  /****************************************************************************/
  /* Write the mimetype file. */

  private void writeMetaInf () throws IOException
  {
    String filePath = Config.instance().getOutputFolderPath() + "../META-INF";
    (new File(filePath)).mkdir();
    filePath = filePath + "/container.xml";
    FileOutputStream f = new FileOutputStream(filePath);
    BufferedWriter w = new BufferedWriter(new OutputStreamWriter(f, "UTF-8"));
    w.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); w.newLine();
    w.write("<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">"); w.newLine();
    w.write("   <rootfiles>"); w.newLine();
    w.write("      <rootfile full-path=\"OPS/content.opf\" media-type=\"application/oebps-package+xml\"/>"); w.newLine();
    w.write("   </rootfiles>"); w.newLine();
    w.write("</container>"); w.newLine();
    w.close();
    f.close();
  }


  /****************************************************************************/
  /* Write the mimetype file. */

  private void writeMimeType () throws IOException
  {
    String filePath = Config.instance().getOutputFolderPath() + "../mimetype";
    FileOutputStream f = new FileOutputStream(filePath);
    BufferedWriter w = new BufferedWriter(new OutputStreamWriter(f, "UTF-8"));
    w.write("application/epub+zip"); w.newLine();
    w.close();
    f.close();
  }


  /****************************************************************************/
  /* Used to handle the header and trailer of the TOC. */

  private void writeTocNcxFile () throws IOException
  {
    String filePath = Config.instance().getOutputFolderPath() + "toc.ncx";
    FileOutputStream f = new FileOutputStream(filePath);
    BufferedWriter w = new BufferedWriter(new OutputStreamWriter(f, "UTF-8"));
    String outputNotesDisposition = Config.instance().getOutputFootnotesDisposition();
    boolean notesCombined = outputNotesDisposition.equalsIgnoreCase("Combined");
    boolean notesPerTextFile = outputNotesDisposition.equalsIgnoreCase("PerTextFile");

    writeTocNcxFileHeader(w);

    int n = 0;

    /*------------------------------------------------------------------------*/
    BookAssemblyAllOutputFilesIterator it = new BookAssemblyAllOutputFilesIterator();
    while (it.hasNext())
    {
      BookElement be = it.next();
      if ("scripture".equalsIgnoreCase(be.getType()))
      {
        String s = be.getVernacularString();
        if (null != s)
        {
          String fullVernacularName = BibleDetails.instance().getFullVernacularName_V(s);
          writeTocNcxFileItem(w, ++n, fullVernacularName, be.getOutputFileName(0));
          if (notesPerTextFile)
            writeTocNcxFileItem
              (w,
               ++n,
               fullVernacularName + "Notes",
               be.getFootnotesFileName(0));
        }
      }
      else
      {
        String s = be.getVernacularStringSelector();
        String outputFileName = be.getInputFileName();
        if ("notes".equalsIgnoreCase(s) && null == outputFileName) outputFileName = Config.instance().getOutputFootnotesFileName();
        if ("tableOfContents".equalsIgnoreCase(s) && null == outputFileName) outputFileName = Config.instance().getOutputTocFileName();
        if (!"notes".equalsIgnoreCase(s) || notesCombined)
        {
          if (0 != outputFileName.length()) writeTocNcxFileItem(w, ++n, s, outputFileName);
        }
      }
    }

    writeTocNcxFileTrailer(w);

    w.close();
    f.close();
  }


  /****************************************************************************/
  /* Write the header for the toc.ncx file. */

  private void writeTocNcxFileHeader (BufferedWriter w) throws IOException
  {
    w.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); w.newLine();
    w.write("<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\" \"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">"); w.newLine();
    w.write("<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" xml:lang=\"en\" version=\"2005-1\">"); w.newLine();
    w.write("<!-- "); w.write(m_RunDateAndTime); w.write(" -->"); w.newLine();
    w.write("  <head>"); w.newLine();
    w.write("    <meta name=\"dtb:uid\" content=\"" + m_Uuid.toString() + "\"/>"); w.newLine();
    w.write("    <meta name=\"dtb:depth\" content=\"1\"/>"); w.newLine();
    w.write("    <meta name=\"dtb:totalPageCount\" content=\"0\"/>"); w.newLine();
    w.write("    <meta name=\"dtb:maxPageNumber\" content=\"0\"/>"); w.newLine();
    w.write("  </head>"); w.newLine();
    w.write("  <docTitle><text>" + Config.instance().getDcTitle() + "</text></docTitle>"); w.newLine();
    w.write("  <navMap>"); w.newLine();
  }


  /****************************************************************************/
  /* Write an individual item for the toc.ncx file. */

  private void writeTocNcxFileItem (BufferedWriter w,
                                    int itemNo,
                                    String description,
                                    String fileName) throws IOException
  {
    w.write("    <navPoint id=\"navpoint-" + itemNo + "\" playOrder=\"" + itemNo + "\">"); w.newLine();
    w.write("      <navLabel>"); w.newLine();
    w.write("        <text>" + description + "</text>"); w.newLine();
    w.write("      </navLabel>"); w.newLine();
    w.write("      <content src=\"" + fileName + "\"/>"); w.newLine();
    w.write("    </navPoint>"); w.newLine();
  }


  /****************************************************************************/
  /* Write the header for the toc.ncx file. */

  private void writeTocNcxFileTrailer (BufferedWriter w) throws IOException
  {
    w.write("  </navMap>"); w.newLine();
    w.write("</ncx>"); w.newLine();
  }


  /****************************************************************************/
  /* Zip everything up. */

  private void zip () throws IOException
  {
    String zipPath = Config.instance().getZipFilePath();
    Zip zipper = new Zip();
    String[] inputs = {"mimetype¬Store", "META-INF", "OPS"};
    zipper.create(zipPath, 9, Config.instance().getOutputFolderPath() + "..", inputs);
  }


  /****************************************************************************/
  /* UUID. */

  private UUID m_Uuid = UUID.randomUUID();
}

